{
  for presentation of quizes
}

unit TrainPresent;

interface

uses StdCtrls,ExtCtrls,Controls,Dialogs,MPlayer,
     Graphics,SysUtils,Classes,Windows,
     xmldom, XMLIntf, msxmldom, XMLDoc,
     jpeg,ShockwaveFlashObjects_TLB,
     XmlWrap, lsComponent, TextKeyboardSelect, ImageKeyboardSelect,
  WebBrowserFrm;

type
  TTrainPresent=class
  private
    FTrainConfig: TXMLWrap;
    FInstance: TXMLWrap;
    FComponents: TList;
    FIntroFile: string;
    FShockWaveFlash: TShockWaveFlash;
    FHTMLBrowser: TWebBrowserForm;
    FPathOfTrain: string;
    FCurQuizID: Integer;
    FTarget: string;
    FQuizStartTime: TDateTime;
    FQuizEndTime: TDateTime;
    FResponseTime: Double;
    FCurCorrect: string;
    FCurAnswer: string;
    FInQuiz: Boolean;
    FUserID: string;
    FTrainID: string;
    FTimeLimit: Integer;
    FRightCount: Integer;
    FWrongCount: Integer;
    FContainer: TWinControl;
    FNextTimer: TTimer;
    FStartTime: Integer;
    FEndTime: Integer;
    FIsRandom: string;
    FQuizDelay: Integer;
    FQuizeRandom: Boolean;
    FStartTicket: Cardinal;
    Ftimepresenter: Boolean;
    FTimeLable: TLabel;
    FOnEnd: TNotifyEvent;
    FIsCycle: Boolean;
    FOnStart: TNotifyEvent;
    FTrainName: string;

    procedure CreateComponents;
    procedure FreeComponents;
    procedure AddComponent(AlsCom: TlsComponent);
    procedure RecvEvent(AMsg: String);
    procedure FireEvent(AEvent: String);
    procedure RefreshInstance(AIndex: Integer);
    procedure RefreshComponents;
    procedure WaitForNext(Sender: TObject);

    procedure RandomQuizes(ANode: IXMLNode);

    function  BriefOfNode(ANode: IXMLNode): String;
    procedure SetPathOfTrain(const Value: string);
    procedure Writelog;
    procedure SetTimeLimit(const Value: Integer);
    procedure SetTrainID(const Value: string);
    procedure SetUserID(const Value: string);
    procedure PresentQuiz(AIndex: Integer);
    procedure EndQuiz;
    procedure ShowIntroduce;
    procedure HideIntroduce;
    function GetProcess: Double;
    procedure SetOnEnd(const Value: TNotifyEvent);
    procedure SetOnStart(const Value: TNotifyEvent);
    procedure SetTrainName(const Value: string);
  public
    constructor Create(AContainer: TWinControl);
    destructor  Destroy; override;
    procedure Load(const AFileName: string);
    function  QuizCount: Integer;
    function  InQuiz: Boolean;
    procedure Start;
    procedure Next;
    procedure Stop;

    property RightCount: Integer read FRightCount;
    property WrongCount: Integer read FWrongCount;
    property PathOfTrain: string read FPathOfTrain write SetPathOfTrain;
    property UserID: string read FUserID write SetUserID;
    property TrainID: string read FTrainID write SetTrainID;
    property TrainName: string read FTrainName write SetTrainName;
    property TimeLimit: Integer read FTimeLimit write SetTimeLimit;
    property CurQuizID: Integer read FCurQuizID;
    property StartTime: Integer read FStartTime;
    property EndTime: Integer read FEndTime;
    property Process: Double read GetProcess;
    property OnEnd: TNotifyEvent read FOnEnd write SetOnEnd;
    property OnStart: TNotifyEvent read FOnStart write SetOnStart;
    property Target: String read FTarget;
  end;

  TlsTimer = class
  private
   FCheckPoint:DWord;
  public
    constructor Create;
    destructor  Destroy;override;
    procedure WaitFor(Time: Integer);
    procedure SetCheckPoint;
    procedure WaitTo(ATime: Integer);
    function GetCheckPoint: DWord;
  end;


  TlsText = class(TlsComponent)
  private
    FText: TLabel;
  protected
    procedure innerDoAction(AAction: String);override;
  public
    constructor Create(AParent: TWinControl;AlsConfig: IXMLNode);override;
    destructor  Destroy;override;
    procedure SetProperty(ANode: IXMLNode);override;
  end;

  TlsImage = class(TlsComponent)
  private
    FImage: TImage;
  protected
    procedure innerDoAction(AAction: String);override;
  public
    constructor Create(AParent: TWinControl;AlsConfig: IXMLNode);override;
    destructor Destroy;override;
    procedure SetProperty(ANode: IXMLNode);override;
  end;

  TlsListBox = class(TlsComponent)
  private
    FListBox: TListBox;
  protected
    procedure innerDoAction(AAction: String); override;
  public
    constructor Create(AParent: TWinControl;AlsConfig: IXMLNode);override;
    destructor  Destroy;override;
    procedure SetProperty(ANode: IXMLNode);override;
  end;

  TlsSingleChoice = class(TlsComponent)
  private
    FAnswer: String;
    FRadioButtons: array[0..3] of TRadioButton;
    procedure SetLeft(AValue: Integer);
    procedure SetTop(AValue: Integer);
    procedure SetVisible(AVisible: Boolean);
    function  FindAnswerButton: TRadioButton;
    procedure ShowAnswer;
    procedure ResetButtons;
    procedure Click(ASender: TObject);
  protected
    procedure innerDoAction(AAction: String); override;
  public
    constructor Create(AParent: TWinControl;AlsConfig: IXMLNode);override;
    destructor  Destroy;override;
    procedure SetProperty(ANode: IXMLNode);override;
  end;


implementation

uses
  SpeechRecognitionFeedback, Math, LogFile, Variants, ImageClick, CommonUtils,
  SpeechRepeat, lsAudioPlayer, FlashPlay, lsAudioButton, Forms, SpeechScore,
  ImageMultiSelect, lsCloze, TextMultiSelect, ImageMultiClick;

{ TlsText }
constructor TlsText.Create(AParent: TWinControl;AlsConfig: IXMLNode);
begin
  inherited;

  FText:=TLabel.Create(FParent);
  FText.Parent:=FParent;
  FText.Visible:=False;
end;

destructor TlsText.Destroy;
begin
  FText.Free;
  inherited;
end;



procedure TlsText.InnerDoAction(AAction: String);
begin
  inherited;
  if AAction = 'show' then
    FText.Show
  else if AAction = 'hide' then
    FText.Hide;
end;

procedure TlsText.SetProperty(ANode: IXMLNode);
begin
  FText.Left:= GetChildNodeValue(ANode, 'left');
  FText.Top:= GetChildNodeValue(ANode, 'top');
  FText.Width:= GetChildNodeValue(ANode, 'width');
  FText.Height:= GetChildNodeValue(ANode, 'height');
  FText.Caption := GetChildNodeValue(ANode, 'text');
  FText.Font.Size := GetChildNodeValue(ANode, 'size');
end;

{ TlsImage }

constructor TlsImage.Create(AParent: TWinControl;AlsConfig: IXMLNode);
begin
  inherited;

  FImage:=TImage.Create(AParent);
  FImage.Parent:=AParent;
  FImage.Visible:=False;
end;

destructor TlsImage.Destroy;
begin
  FImage.Free;
  inherited;
end;


procedure TlsImage.InnerDoAction(AAction: String);
begin
  inherited;
  if AAction = 'show' then
    FImage.Show
  else if AAction = 'hide' then
    FImage.Hide;
end;

procedure TlsImage.SetProperty(ANode: IXMLNode);
var
  FileName1: String;
begin
  FImage.Left:= GetChildNodeValue(ANode, 'left');
  FImage.Top:= GetChildNodeValue(ANode, 'top');
  FImage.Width:= GetChildNodeValue(ANode, 'width');
  FImage.Height:= GetChildNodeValue(ANode, 'height');
  FileName1 := PathOfTrain + GetChildNodeValue(ANode, 'filename');
  if FileExists(FileName1) then
    FImage.Picture.LoadFromFile(FileName1)
  else
    ShowMessage('File:'+FileName1+' not exists');
end;


{TlsTimer}
procedure TlsTimer.SetCheckPoint;
begin
  FCheckPoint:=GetTickCount;
end;

procedure TlsTimer.WaitFor(Time: Integer);
var T1,T2: DWord;
begin
  T1:=GetTickCount;
  while True do begin
    //Application.ProcessMessages;
    T2:=GetTickCount;
    if (T2-T1)>Time then break;
  end;
end;

//wait from checkpoint to ...
procedure TlsTimer.WaitTo(ATime: Integer);
var T1: TDateTime;
begin
  while True do begin
    //Application.ProcessMessages;
    T1:=GetTickCount;
    if (T1-FCheckPoint)>ATime then break;
  end;

end;

constructor TlsTimer.Create;
begin

end;

destructor TlsTimer.Destroy;
begin
  inherited;
end;

function TlsTimer.GetCheckPoint: DWord;
begin
  Result:=FCheckPoint;
end;


{ TQuizPresentForm }

procedure TTrainPresent.AddComponent(AlsCom: TlsComponent);
begin
  FComponents.Add(AlsCom);
end;

function TTrainPresent.BriefOfNode(ANode: IXMLNode): String;
var i:Integer;
    Node1: IXMLNode;
begin
  Result:='';
  for i := 0 to ANode.ChildNodes.Count - 1 do begin
    Node1:=ANode.ChildNodes.Get(i);
    if Node1.NodeType=ntText then begin
      Result:=Result+' '+Node1.NodeValue;
    end else if Node1.NodeType=ntElement then begin
      Result:=Result+' '+Node1.NodeValue;
    end;
  end;
end;

constructor TTrainPresent.Create(AContainer: TWinControl);
begin
  FNextTimer := TTimer.Create(nil);
  FNextTimer.Enabled := False;
  FTrainConfig := TXmlWrap.Create;
  FInstance:=TXMLWrap.Create;
  FContainer := AContainer;
  FComponents:=TList.Create;
  FShockWaveFlash:=TShockWaveFlash.Create(nil);
  FHTMLBrowser := TWebBrowserForm.Create(nil);
end;

procedure TTrainPresent.CreateComponents;
var
    i,Count1: Integer;
    Type1: String;
    lsCom1: TlsComponent;
    List1: IXMLNodeList;
    Node1: IXMLNode;
begin
  List1:=FInstance.RootNode.ChildNodes.FindNode('components').ChildNodes;
  Count1:=List1.Count;
  lsCom1 := nil;
  for i := 0 to Count1-1 do
  begin
    Node1:=List1.Get(i);
    Type1:=Node1.ChildNodes.FindNode('type').NodeValue;
    if Type1='text' then
       lsCom1:=TlsText.Create(FContainer,Node1)
    else if Type1='image' then
      lsCom1:=TlsImage.Create(FContainer,Node1)
    else if Type1='listbox' then
      lsCom1:=TlsListBox.Create(FContainer,Node1)
    else if Type1='singlechoice' then
      lsCom1:=TlsSingleChoice.Create(FContainer,Node1)
    else if Type1='audioplayer' then
      lsCom1:=TlsAudioPlayer.Create(FContainer,Node1)
    else if Type1 = 'TTextKeyboardSelect' then
      lsCom1 := TTextKeyboardSelect.Create(FContainer, Node1)
    else if Type1 = 'TImageMultiSelect' then
      lsCom1 := TImageMultiSelect.Create(FContainer, Node1)
    else if Type1 = 'TImageKeyboardSelect' then
      lsCom1 := TImageKeyboardSelect.Create(FContainer, Node1)
    else if Type1 = 'TSpeechRecognitionFeedback' then
      lsCom1 := TSpeechRecognitionFeedback.Create(FContainer, Node1)
    else if Type1 = 'TImageClick' then
      lsCom1 := TImageClick.Create(FContainer, Node1)
    else if Type1 = 'TSpeechRepeat' then
      lsCom1 := TSpeechRepeat.Create(FContainer, Node1)
    else if Type1 = 'TlsAudioPlayer' then
      lsCom1 := TlsAudioPlayer.Create(FContainer, Node1)
    else if Type1 = 'TFlashPlay' then
      lsCom1 := TFlashPlay.Create(FContainer, Node1)
    else if Type1 = 'TlsAudioButton' then
      lsCom1 := TlsAudioButton.Create(FContainer, Node1)
    else if Type1 = 'TSpeechScore' then
      lsCom1 := TSpeechScore.Create(FContainer, Node1)
    else if Type1 = 'TCloze' then
      lsCom1 := TlsCloze.Create(FContainer, Node1)
    else if Type1 = 'TTextMultiSelect' then
      lsCom1 := TTextMultiSelect.Create(FContainer, Node1)
    else if Type1 = 'TImageMultiClick' then
      lsCom1 := TImageMultiClick.Create(FContainer, Node1)
    else
      ShowMessage('undefined type:'+Type1);

    if lsCom1 <> nil then
    begin
      lsCom1.PathOfTrain := PathOfTrain;
      lsCom1.OnlsEvent:= RecvEvent;
      AddComponent(lsCom1);
      lsCom1.Refresh;
    end;
  end;
end;

destructor TTrainPresent.Destroy;
begin
  FreeAndNil(FShockWaveFlash);
  FreeAndNil(FHTMLBrowser);
  FreeAndNil(FNextTimer);
  FreeComponents;
  if FTrainConfig <> nil then
    FreeAndNil(FTrainConfig);
  if FInstance <> nil then
    FreeAndNil(FInstance);

  inherited;
end;

procedure TTrainPresent.EndQuiz;
begin
  FireEvent('unload');
  FQuizEndTime := Now;
  Writelog;
  FInQuiz := False;
  if Assigned(FOnEnd) then
    FOnEnd(Self); 
end;

procedure TTrainPresent.FireEvent(AEvent: String);
var
  i:Integer;
  lsCom1: TlsComponent;
begin
  for i := 0 to FComponents.Count-1 do
  begin
    lsCom1:=FComponents.Items[i];
    lsCom1.Recv(AEvent);
  end;

  if SameText(AEvent, 'right') then
  begin
    Inc(FRightCount);
    FNextTimer.OnTimer := WaitForNext;
    FNextTimer.Interval := FQuizDelay;
    FNextTimer.Enabled := True;
  end
  else if SameText(AEvent, 'wrong') then
  begin
    Inc(FWrongCount);
    FNextTimer.OnTimer := WaitForNext;
    FNextTimer.Interval := FQuizDelay;
    FNextTimer.Enabled := True;   
  end;
end;

procedure TTrainPresent.FreeComponents;
var i:Integer;
begin
  for i := 0 to FComponents.Count-1 do
    TLsComponent(FComponents.Items[i]).Free;
  FComponents.Free;
end;

function TTrainPresent.GetProcess: Double;
begin
  Result := (GetTickCount - FStartTicket) / FTimeLimit;
end;

procedure TTrainPresent.HideIntroduce;
begin
  if AnsiPos('.swf', FIntroFile) > 0 then
  begin
    FShockWaveFlash.Stop;
    FShockwaveFlash.Visible:=False;
  end
  else if AnsiPos('.html', FIntroFile) > 0 then
    FHTMLBrowser.Visible := False;
end;

function TTrainPresent.InQuiz: Boolean;
begin
  Result := FInQuiz;
end;

procedure TTrainPresent.Load(const AFileName: string);
begin
  FTrainConfig.LoadFile(AFileName);
  FInstance.Load(FTrainConfig.SelectNode(FTrainConfig.RootNode,'template').XML);
  FInstance.ConvertAllValueToDefault(FInstance.RootNode);
  FIsRandom := FTrainConfig.Get('train/random');
  FQuizDelay := StrToIntDef(FTrainConfig.Get('train/quizdelay'), 500);
  FIntroFile:= PathofTrain + FTrainConfig.Get('train/introduce');
  FQuizeRandom := StrToBoolDef(FTrainConfig.Get('train/random'), False);
  if FQuizeRandom then
    RandomQuizes(FTrainConfig.RootNode.ChildNodes.FindNode('quizes'));
  TPanel(FContainer).Color := StrToIntDef(FTrainConfig.Get('train/maincolor'), clBackground);
  Application.MainForm.Color := StrToIntDef(FTrainConfig.Get('train/formcolor'), clBackground);
  FTimeLimit := MaxInt;
  FTimeLimit := StrToIntDef(FTrainConfig.Get('train/template/timelimit'), FTimeLimit);
  Ftimepresenter := StrToBoolDef(FTrainConfig.Get('train/template/timepresenter'), False);
  FIsCycle := StrToBoolDef(FTrainConfig.Get('train/template/cycles'), False);
  ShowIntroduce;
  CreateComponents;
  if Ftimepresenter then
  begin
    FTimeLable := TLabel.Create(FContainer);
    FTimeLable.Font.Size := 20;
  end;
end;

procedure TTrainPresent.Next;
begin
  if FCurCorrect = '' then
    RecvEvent('wrong');

  FNextTimer.Enabled := False;
  if FCurQuizID < QuizCount - 1 then
  begin
    EndQuiz;
    Inc(FCurQuizID);
    PresentQuiz(FCurQuizID)
  end
  else begin
    if FIsCycle then
    begin
      EndQuiz;
      FCurQuizID := 0;
      PresentQuiz(0);
    end
    else
      Stop;
  end;
end;

procedure TTrainPresent.PresentQuiz(AIndex: Integer);
begin
  FInQuiz := True;
  FCurQuizID := AIndex;
  FCurCorrect := '';
  FCurAnswer := '';
  RefreshInstance(FCurQuizID);
  RefreshComponents;
  FireEvent('load');
  FQuizStartTime := Now;
  if Ftimepresenter then
    FTimeLable.Hide;
  FStartTicket := GetTickCount;
  if Assigned(FOnStart) then
    FOnStart(Self);
end;

function TTrainPresent.QuizCount: Integer;
var Node1: IXMLNode;
begin
  Node1:=FTrainConfig.SelectNode(FTrainConfig.RootNode,'quizes');
  Result:=Node1.ChildNodes.Count;
end;

procedure TTrainPresent.RandomQuizes(ANode: IXMLNode);
var
  i,m:Integer;
  Last1: IXMLNode;
begin
  for i := 0 to ANode.ChildNodes.Count - 1 do
  begin
    m:=Random(ANode.ChildNodes.Count-2);
    Last1:=ANode.ChildNodes.Last;
    ANode.ChildNodes.Remove(Last1);
    ANode.ChildNodes.Insert(m,Last1);
  end;
end;

procedure TTrainPresent.RecvEvent(AMsg: String);
begin
  if SameText(AMsg, 'right') or SameText(AMsg, 'wrong') then
  begin
    FCurCorrect := AMsg;
    FResponseTime := (GetTickCount - FStartTicket)/1000;

    if Ftimepresenter then
    begin
      FTimeLable.Parent := FContainer;
      FTimeLable.Top := 30;
      FTimeLable.Left := FContainer.Width - 100;
      FTimeLable.Caption := FormatFloat('0.00', FResponseTime) + 's';
      FTimeLable.Show;
      FTimeLable.Font.Color := clRed;
    end;
  end;
  FireEvent(AMsg);
end;

procedure TTrainPresent.RefreshComponents;
var i:Integer;
    Com1: TlsComponent;
begin
  for i := 0 to FComponents.Count-1 do
  begin
    Com1:=TlsComponent(FComponents.Items[i]);
    Com1.Refresh;
  end;
end;

procedure TTrainPresent.RefreshInstance(AIndex: Integer);
var
  qs, q: IXMLNode;
  Var1: IXMLNode;
  i: Integer;
  Path1:String;
begin
  qs:=FTrainConfig.SelectNode(FTrainConfig.RootNode,'quizes');
  if (qs = nil) or (qs.ChildNodes.Count <= AIndex) then
    Exit;

  q:=qs.ChildNodes.Get(AIndex);
  FTarget := '';
  if q.ChildNodes.FindNode('target') <> nil then
    FTarget := VarToStrDef(q.ChildNodes.FindNode('target').NodeValue, '');
  FCurAnswer := '';
  if q.ChildNodes.FindNode('answer') <> nil then
    FCurAnswer := VarToStrDef(q.ChildNodes.FindNode('answer').NodeValue, '');

  for i := 0 to q.ChildNodes.Count-1 do begin
    Var1:=q.ChildNodes.Get(i);
    Path1:=FTrainConfig.PathOfVar(FTrainConfig.SelectNode(FTrainConfig.RootNode,'template'),Var1);
    if Path1<>'' then 
      FInstance.AssignValueByPath(FInstance.RootNode,Path1,Var1);

  end;
end;

procedure TTrainPresent.SetOnEnd(const Value: TNotifyEvent);
begin
  FOnEnd := Value;
end;

procedure TTrainPresent.SetOnStart(const Value: TNotifyEvent);
begin
  FOnStart := Value;
end;

procedure TTrainPresent.SetPathOfTrain(const Value: string);
begin
  FPathOfTrain := Value;
end;

procedure TTrainPresent.SetTimeLimit(const Value: Integer);
begin
  FTimeLimit := Value;
end;

procedure TTrainPresent.SetTrainID(const Value: string);
begin
  FTrainID := Value;
end;

procedure TTrainPresent.SetTrainName(const Value: string);
begin
  FTrainName := Value;
end;

procedure TTrainPresent.SetUserID(const Value: string);
begin
  FUserID := Value;
end;

procedure TTrainPresent.ShowIntroduce;
begin
  if AnsiPos('.swf', FIntroFile) > 0 then
  begin
    FShockWaveFlash.Parent:= FContainer;
    FShockWaveFlash.Align := alClient;
    FShockWaveFlash.Movie := FIntroFile;
    FShockWaveFlash.Visible:=True;
    FShockWaveFlash.Play;
    FShockWaveFlash.Playing:=True;
  end
  else if AnsiPos('.html', FIntroFile) > 0 then
  begin
    FHTMLBrowser.Parent := FContainer;
    FHTMLBrowser.Align := alClient;
    FHTMLBrowser.Visible := True;
    FHTMLBrowser.Navigate(FIntroFile);
  end;
end;

procedure TTrainPresent.Start;
begin
  HideIntroduce;
  FRightCount := 0;
  FWrongCount := 0;
  FStartTime := GetTickCount;
  if QuizCount > 0 then
    PresentQuiz(0);    
end;

procedure TTrainPresent.Stop;
begin
  FEndTime := GetTickCount;
  if FInQuiz then
    EndQuiz;
  ShowIntroduce;
end;

procedure TTrainPresent.WaitForNext(Sender: TObject);
begin
  Next;
end;

procedure TTrainPresent.Writelog;
begin
  Logger.LogMessage(Format('%s/%s/%d/%s/%s/%s/%f/%s/%s',
    [FUserID, FTrainName, FCurQuizID, FTarget,
    FormatDateTime('yyyy-mm-dd', FQuizStartTime),
    FormatDateTime('yyyy-mm-dd', FQuizEndTime),
    FResponseTime, FCurCorrect, FCurAnswer]));
end;

{ TlsListBox }

constructor TlsListBox.Create(AParent: TWinControl;AlsConfig: IXMLNode);
begin
  inherited;

  FListBox:=TListBox.Create(AParent);
  FListBox.Parent:=AParent;
  FListBox.Visible:=False;
end;

destructor TlsListBox.Destroy;
begin
  FListBox.Free;

  inherited;
end;

procedure TlsListBox.InnerDoAction(AAction: String);
begin
  inherited;
  if AAction = 'show' then
    FListBox.Show
  else if AAction = 'hide' then
    FListBox.Hide;
end;

procedure TlsListBox.SetProperty(ANode: IXMLNode);
begin
  FListBox.Left:= GetChildNodeValue(ANode, 'left');
  FListBox.Top:= GetChildNodeValue(ANode, 'top');
  FListBox.Width:= GetChildNodeValue(ANode, 'width');
  FListBox.Height:= GetChildNodeValue(ANode, 'height');
  FListBox.Clear;
  GetChildNodeStrings(ANode, 'strings', FListBox.Items);
end;

{ TlsSingleChoice }

procedure TlsSingleChoice.Click(ASender: TObject);
begin
  if TRadioButton(ASender).Caption=FAnswer then begin
    FireEvent('right');
  end else begin
    FireEvent('wrong');
  end;
  ShowAnswer;
end;

constructor TlsSingleChoice.Create(AParent: TWinControl;AlsConfig: IXMLNode);
var
  i:Integer;
begin
  inherited;

  for i := 0 to 3 do begin
    FRadioButtons[i]:=TRadioButton.Create(AParent);
    FRadioButtons[i].Parent:=AParent;
    FRadioButtons[i].OnClick:=Click;
  end;
end;

destructor TlsSingleChoice.Destroy;
var i:Integer;
begin
  for i := 0 to 3 do begin
    FRadioButtons[i].Free;
  end;
  inherited;
end;

function TlsSingleChoice.FindAnswerButton: TRadioButton;
var
  i:Integer;
begin
  Result := nil;
  for i := 0 to 3 do begin
    if FRadioButtons[i].Caption=FAnswer then begin
      Result:=FRadioButtons[i];
    end;
  end;
end;

procedure TlsSingleChoice.InnerDoAction(AAction: String);
begin
  inherited;
  if AAction = 'show' then
    SetVisible(True)
  else if AAction = 'hide' then
    SetVisible(False);
end;

procedure TlsSingleChoice.ResetButtons;
var b1: TRadioButton;
    i: Integer;
begin
  for i := 0 to 3 do begin
    b1:=FRadioButtons[i];
    b1.Font.Style:=[];
    b1.Checked:=False;
  end;

end;

procedure TlsSingleChoice.SetProperty(ANode: IXMLNode);
var
  i:Integer;
  slText: TStringList;
begin
  FAnswer := GetChildNodeValue(ANode, 'answer');
  SetLeft(GetChildNodeValue(ANode, 'left'));
  SetTop(GetChildNodeValue(ANode, 'top'));
  slText := TStringList.Create;
  try
    GetChildNodeStrings(ANode, 'strings', slText);
    for I := 0 to Min(4, slText.Count) - 1 do
      FRadioButtons[I].Caption := slText[I];
    for I := slText.Count to 3 do
      FRadioButtons[I].Caption := '';
  finally
    FreeAndNil(slText);
  end;
end;

procedure TlsSingleChoice.SetTop(AValue: Integer);
var i,Top1:Integer;
begin
  Top1:=AValue;
  for i := 0 to Length(FRadioButtons)-1 do begin
    FRadioButtons[i].Top:=Top1;
    Top1:=Top1+FRadioButtons[i].Height+2;
  end;
end;

procedure TlsSingleChoice.SetVisible(AVisible: Boolean);
var i:Integer;
begin
  ResetButtons;
  for i := 0 to Length(FRadioButtons)-1 do begin
    if FRadioButtons[i].Caption<>'' then begin
      FRadioButtons[i].Visible:=AVisible;
    end;
  end;
end;

procedure TlsSingleChoice.ShowAnswer;
var b1: TRadioButton;
begin
  b1:=FindAnswerButton;
  b1.Checked:=True;
  b1.Font.Style:=[fsBold];
end;

procedure TlsSingleChoice.SetLeft(AValue: Integer);
var i:Integer;
begin
  for i := 0 to Length(FRadioButtons)-1 do begin
    FRadioButtons[i].Left:=AValue;
  end;
end;

end.
